#include <iostream>
#include <string>
#include <vector>
#include <cassert>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include "Task.hpp"

const int num = 5; // 最大管道数
int number = 0;    // 管道编号

class channel
{
public:
    channel(int fd, pid_t id)
        : ctrlfd(fd), workerid(id)
    {
        name = "channel-" + std::to_string(++number);
    }

public:
    int ctrlfd;         // 管道写端
    pid_t workerid;     // 对应的子进程pid
    std::string name;   // 管道名
};

void Work()
{
    while (true)
    {
        int code = 0;   // 任务码
        ssize_t n = read(0, &code, sizeof(code));
        if (n == sizeof(code))
        {
            if (!init.CheckSafe(code))
                continue;
            init.RunTask(code);
        }
        else if (n == 0)
        {
            // n == 0 写端退出
            break;
        }
        else
        {
            // do nothing
        }
    }
    std::cout << "child quit" << std::endl;
}

// 打印都关闭了哪些从父进程继承下来的写端
void PrintFd(const std::vector<int> &fds)
{
    std::cout << getpid() << " close: ";
    for (auto fd : fds)
    {
        std::cout << fd << " ";
    }
    std::cout << std::endl;
}

// 传参形式：
// 1. 输入参数：const &
// 2. 输出参数：*
// 3. 输入输出参数：&

void CreatChannels(std::vector<channel> *c)
{
    std::vector<int> old;
    for (int i = 0; i < num; i++)
    {
        // 1. 定义并创建管道
        int pipefd[2];
        int n = pipe(pipefd);
        assert(n == 0);
        (void)n;

        // 2. 创建进程
        pid_t id = fork();
        assert(id != -1);

        // 3. 构建单向通信信道
        if (id == 0)
        {
            // child

            // 关闭从父进程继承下来的写端
            if (!old.empty())
            {
                for (auto fd : old)
                {
                    close(fd);
                }
                // debug
                PrintFd(old);
            }

            close(pipefd[1]); // 关闭写端
            // TODO
            dup2(pipefd[0], 0); // 重定向
            Work();
            exit(0);    // 会自动关闭自己打开的所有fd
        }

        // father
        close(pipefd[0]); // 关闭读端
        c->push_back(channel(pipefd[1], id));
        old.push_back(pipefd[1]);
    }

    // bug version

    // for (int i = 0; i < num; i++)
    // {
    //     // 1. 定义并创建管道
    //     int pipefd[2];
    //     int n = pipe(pipefd);
    //     assert(n == 0);
    //     (void)n;

    //     // 2. 创建进程
    //     pid_t id = fork();
    //     assert(id != -1);

    //     // 3. 构建单向通信信道
    //     if (id == 0)
    //     {
    //         // child
    //         close(pipefd[1]); // 关闭写端
    //         // TODO
    //         dup2(pipefd[0], 0); // 重定向
    //         Work();
    //         exit(0);    // 会自动关闭自己打开的所有fd
    //     }

    //     // father
    //     close(pipefd[0]); // 关闭读端
    //     c->push_back(channel(pipefd[1], id));
    // }
}

void PrintfDebug(const std::vector<channel> &c)
{
    for (const auto &channel : c)
    {
        std::cout << channel.name << ", " << channel.ctrlfd << ", " << channel.workerid << std::endl;
    }
}

const bool g_always_loop = true;    // 是否一直执行

void SendCommand(const std::vector<channel> &channels, bool flag, int num = -1)
{
    int pos = 0;
    while (true)
    {
        // 1. 选择任务
        int command = init.SelectTask();
        // 2. 选择信道(进程)
        const auto &c = channels[pos++];
        pos %= channels.size();

        // debug
        std::cout << "send command " << init.ToDesc(command) << "[" << command << "]" 
                  << " in" << c.name << "worker is: " << c.workerid << std::endl;

        // 3. 发送任务
        write(c.ctrlfd, &command, sizeof(command));
        // 4. 判断是否要退出
        if (!flag)
        {
            num--;
            if (num <= 0) break;
        }

        sleep(1);
    }

    std::cout << "SendCommand done..." << std::endl;
}

void ReleaseChannels(const std::vector<channel> &channels)
{
    // version2
    int num = channels.size() - 1;
    for (; num >= 0; num--)
    {
        close(channels[num].ctrlfd);
        pid_t rid = waitpid(channels[num].workerid, nullptr, 0);
        if (rid == channels[num].workerid)
        {
            std::cout << "wait child: " << channels[num].workerid << " success" << std::endl;
        }
    }

    // version1
    // for (const auto &c : channels)
    // {
    //     close(c.ctrlfd);
    // }

    // for (const auto &c : channels)
    // {
    //     pid_t rid = waitpid(c.workerid, nullptr, 0);
    //     if (rid == c.workerid)
    //     {
    //         std::cout << "wait child: " << c.workerid << " success" << std::endl;
    //     }
    // }

    // bug version
    // for (const auto &c : channels)
    // {
    //     close(c.ctrlfd);
    //     pid_t rid = waitpid(c.workerid, nullptr, 0);
    //     if (rid == c.workerid)
    //     {
    //         std::cout << "wait child: " << c.workerid << " success" << std::endl;
    //     }
    // }
}

int main()
{
    std::vector<channel> channels;
    // 创建信道，创建进程
    CreatChannels(&channels);

    // 开始发送任务
    SendCommand(channels, !g_always_loop, 10);
    
    // PrintfDebug(channels);
    // sleep(10);

    // 回收资源，想让子进程退出，并且释放管道资源，只要关闭写端即可(写端关闭后，子进程自动退出)
    ReleaseChannels(channels);

    return 0;
}